package com.levy.swrb.core.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.levy.swrb.core.model.Node;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 节点计算
 */
public class NodeCalculateUtil {

    public static List<String> calculateIdList(List<Node> nodeList) {
        Assert.notEmpty(nodeList);
        if (nodeList.size() == 1) {
            return nodeList.stream().map(Node::getId).collect(Collectors.toList());
        }
        List<Integer> weightList = nodeList.stream().map(Node::getWeight).collect(Collectors.toList());

        int totalWeight = nodeList.stream().map(Node::getWeight).reduce(0, Integer::sum);
        // 计算一轮循环需要的最小次数
        int gcd = gcdOfArray(weightList);
        int roundTimes = totalWeight / gcd;

        List<String> idList = new ArrayList<>(roundTimes);

        // 复制 nodeList
        List<Node> copyNodeList = CollUtil.newArrayList(nodeList);

        for (int i = 0; i < roundTimes; i++) {
            String nodeId = calculateNodeId(copyNodeList, totalWeight);
            idList.add(nodeId);
        }

        return idList;
    }

    public static String calculateNodeId(List<Node> nodeList, int totalWeight) {
        Assert.notEmpty(nodeList);
        Node maxCurrent = null;
        for (Node node : nodeList) {
            node.setCurrent(node.getCurrent() + node.getWeight());
            if (maxCurrent == null || node.getCurrent() > maxCurrent.getCurrent()) {
                maxCurrent = node;
            }
        }
        // 前面验证了列表非空，纯是为了消除编辑器的警告
        assert maxCurrent != null;
        maxCurrent.setCurrent(maxCurrent.getCurrent() - totalWeight);
        return maxCurrent.getId();
    }

    /**
     * 求最大公约数
     */
    public static int gcd(int a, int b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

    /**
     * 求数组的最大公约数
     */
    public static int gcdOfArray(List<Integer> arr) {
        Assert.notEmpty(arr);
        if (arr.size() == 1) {
            return arr.get(0);
        }
        return arr.stream().reduce(gcd(arr.get(0), arr.get(1)), NodeCalculateUtil::gcd);
    }

}
